import java.util.concurrent.atomic.AtomicInteger;

public class HighConcurrencyCouponCodeGenerator {

    private static final int SEQUENCE_BITS = 12; // 序列号占用的位数
    private static final long SEQUENCE_MASK = -1L >>> (64 - SEQUENCE_BITS); // 序列号掩码
    private static final AtomicInteger workerId = new AtomicInteger(0); // 工作节点ID

    private static final long WORKER_ID_BITS = 5; // 工作节点ID占用的位数
    private static final long MAX_WORKER_ID = -1L >>> (64 - WORKER_ID_BITS); // 最大工作节点ID
    private static final long SEQUENCE_SHIFT = WORKER_ID_BITS; // 序列号左移位数
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; // 时间戳左移位数

    private static final long EPOCH = 1577836800000L; // 起始时间戳，2020-01-01 00:00:00 UTC
    private static volatile long lastTimestamp = -1L; // 上次时间戳
    private static volatile long sequence = 0L; // 序列号

    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    private static long nextId() {
        long timestamp = timeGen();

        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        // 如果是同一时间生成，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 毫秒内序列溢出
            if (sequence == 0) {
                // 阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else { // 时间戳改变，毫秒内序列重置
            sequence = 0L;
        }

        // 上次生成ID的时间截
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) |
                (workerId.get() << SEQUENCE_SHIFT) |
                sequence;
    }

    private static long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 生成14位优惠券编码
     * @return 14位数字编码
     */
    public static String generateCouponCode() {
        long id = nextId();
        // 将64位的ID转换为14位的字符串
        return String.format("%014d", id);
    }

    public static void main(String[] args) {
        // 测试生成的优惠券编码
        for (int i = 0; i < 10; i++) {
            System.out.println(generateCouponCode());
        }
    }
}
